/**
 * @file        spi_sdcard.c
 * @brief       SD卡SPI模式驱动实现文件
 * @details     基于SPI接口的SD卡驱动程序实现，支持SD V1.x/V2.x/SDHC/MMC卡
 *              实现SD卡的初始化、读写、状态检测等核心功能
 *
 * @par 技术特性:
 *              - 支持SD V1.x、SD V2.x、SDHC、MMC等多种卡类型识别
 *              - 基于SPI接口通信，兼容性好
 *              - 支持单扇区和多扇区读写操作
 *              - 完整的错误检测和处理机制
 *              - 兼容FATFS文件系统接口
 *
 * @par 实现原理:
 *              - 初始化时使用低速SPI模式进行卡识别
 *              - 通过发送标准SD命令进行卡类型检测
 *              - 根据卡类型选择合适的地址模式和容量计算方法
 *              - 读写操作支持数据完整性校验
 */

#include "./BSP/SDMMC/spi_sdcard.h"
#include <stdio.h>

/* ----------------------------------- 全局变量定义 ----------------------------------------- */

/**
 * @brief SD卡类型全局变量
 * @note  保存当前检测到的SD卡类型，用于后续读写操作中的地址转换
 *        可能的值: SD_TYPE_ERR, SD_TYPE_MMC, SD_TYPE_V1, SD_TYPE_V2, SD_TYPE_V2HC
 */
uint8_t  sd_type = 0;

/* ----------------------------------- 内部静态函数实现 ----------------------------------- */

/**
 * @brief       SD卡SPI硬件层初始化
 * @param       无
 * @return      无
 * @note        初始化SD卡片选引脚和SPI接口
 *              同时禁用其他SPI设备的片选信号，防止总线冲突
 *              PA2和PA4用于禁用NRF24L01和W25Q64等设备
 */
static void sd_spi_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    SD_CS_GPIO_CLK_ENABLE();                            /* 使能SD卡片选引脚时钟 */
    gpio_init_struct.Pin = SD_CS_GPIO_PIN;              /* 配置SD卡片选引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出模式 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 内部上拉电阻 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速翻转速率 */
    HAL_GPIO_Init(SD_CS_GPIO_PORT, &gpio_init_struct);  /* 初始化SD卡片选引脚 */

    /* 配置其他SPI设备的片选引脚，防止总线冲突 */
    __HAL_RCC_GPIOA_CLK_ENABLE();                       /* 使能GPIOA时钟 */
    gpio_init_struct.Pin = GPIO_PIN_2 | GPIO_PIN_4;     /* PA2(NRF24L01_CS), PA4(W25Q64_CS) */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出模式 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 内部上拉电阻 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速翻转速率 */
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);            /* 初始化其他设备片选引脚 */
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); /* 禁用NRF24L01(拉高片选) */
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); /* 禁用W25Q64(拉高片选) */

    spi1_init();    /* 初始化SPI1接口 */
    SD_CS(1);       /* SD卡片选置高，默认不选中 */
}

/**
 * @brief       SD卡取消选择，释放SPI总线
 * @param       无
 * @return      无
 * @note        拉高片选信号并发送8个时钟脉冲
 *              确保SD卡完成当前操作并释放SPI总线
 */
static void sd_deselect(void)
{
    SD_CS(1);                       /* 取消SD卡片选 */
    sd_spi_read_write_byte(0xff);   /* 提供额外的8个时钟 */
}

/**
 * @brief       SD卡选中并等待卡准备就绪
 * @param       无
 * @return      选中结果
 * @retval      SD_OK       成功选中SD卡
 * @retval      SD_ERROR    选中失败或卡未就绪
 * @note        拉低片选信号并等待SD卡返回0xFF(就绪状态)
 *              如果等待超时则自动取消选择
 */
static uint8_t sd_select(void)
{
    SD_CS(0);

    if (sd_wait_ready() == 0)
    {
        return SD_OK;   /* 等待成功 */
    }

    sd_deselect();
    return SD_ERROR;    /* 等待失败 */
}

/**
 * @brief       等待SD卡准备就绪
 * @param       无
 * @return      等待结果
 * @retval      SD_OK       SD卡已就绪
 * @retval      SD_ERROR    等待超时
 * @note        SD卡就绪时会返回0xFF，忙碌时返回其他值
 *              最大等待时间约16.7秒(0xFFFFFF次循环)
 */
static uint8_t sd_wait_ready(void)
{
    uint32_t t = 0;

    do
    {
        if (sd_spi_read_write_byte(0XFF) == 0XFF)
        {
            return SD_OK;   /* OK */
        }

        t++;
    } while (t < 0XFFFFFF); /* 等待 */

    return SD_ERROR;
}

/**
 * @brief       等待SD卡特定响应
 * @param       response 期待的响应值
 * @return      等待结果
 * @retval      SD_OK       收到期待的响应
 * @retval      SD_ERROR    等待超时
 * @note        用于等待SD卡命令执行后的特定响应码
 *              最大等待65535次SPI传输
 */
static uint8_t sd_get_response(uint8_t response)
{
    uint16_t count = 0xFFFF;    /* 等待次数 */

    while ((sd_spi_read_write_byte(0XFF) != response) && count)
    {
        count--;    /* 等待得到准确的回应 */
    }

    if (count == 0) /* 等待超时 */
    {
        return SD_ERROR;
    }

    return SD_OK;   /* 正确回应 */
}

/**
 * @brief       从SD卡接收数据
 * @param       buf 数据缓存区指针
 * @param       len 要读取的数据长度(字节数)
 * @return      读取结果
 * @retval      SD_OK       数据接收成功
 * @retval      SD_ERROR    接收失败或超时
 * @note        等待数据起始令牌0xFE后开始接收数据
 *              接收完数据后会读取2字节CRC校验码(但不验证)
 *              通常用于接收512字节的扇区数据
 */
static uint8_t sd_receive_data(uint8_t *buf, uint16_t len)
{
    if (sd_get_response(0xFE))   /* 等待数据起始令牌0xFE */
    {
        return SD_ERROR;
    }

    while (len--)   /* 逐字节接收数据 */
    {
        *buf = sd_spi_read_write_byte(0xFF);    /* 发送0xFF读取一字节数据 */
        buf++;                                  /* 缓存区指针递增 */
    }

    /* 读取2字节CRC校验码(但不验证) */
    sd_spi_read_write_byte(0xFF);               /* 读取CRC高字节 */
    sd_spi_read_write_byte(0xFF);               /* 读取CRC低字节 */

    return SD_OK;   /* 读取成功 */
}

/**
 * @brief       向SD卡发送数据块
 * @param       buf 数据缓存区指针
 * @param       cmd 数据令牌(0xFE-单块写入, 0xFC-多块写入, 0xFD-停止传输)
 * @return      发送结果
 * @retval      SD_OK       数据发送成功
 * @retval      SD_ERROR    发送失败
 * @note        固定发送512字节数据块
 *              发送数据前需等待SD卡就绪
 *              发送后检查SD卡响应确认数据被接收
 */
static uint8_t sd_send_block(uint8_t *buf, uint8_t cmd)
{
    uint16_t t;

    if (sd_wait_ready())            /* 等待SD卡就绪 */
    {
        return SD_ERROR;
    }

    sd_spi_read_write_byte(cmd);    /* 发送数据令牌 */

    if (cmd != 0XFD)                /* 非停止传输令牌，需要发送数据 */
    {
        for (t = 0; t < 512; t++)
        {
            sd_spi_read_write_byte(buf[t]); /* 逐字节发送512字节数据 */
        }

        sd_spi_read_write_byte(0xFF);       /* 发送伪CRC校验码(高字节) */
        sd_spi_read_write_byte(0xFF);       /* 发送伪CRC校验码(低字节) */

        t = sd_spi_read_write_byte(0xFF);   /* 接收SD卡数据响应令牌 */

        if ((t & 0x1F) != 0x05)             /* 检查数据接收状态(xxx00101) */
        {
            return SD_ERROR;
        }
    }

    return SD_OK;   /* 写入成功 */
}

/**
 * @brief       向SD卡发送命令
 * @param       cmd 要发送的命令索引
 * @param       arg 命令参数(32位)
 * @return      SD卡响应值
 * @note        命令格式: 起始位(0) + 传输位(1) + 命令索引(6位) + 参数(32位) + CRC(7位) + 结束位(1)
 *              最高位为1表示ACMD(应用命令)，需先发送CMD55
 *              最高位为0表示CMD(普通命令)
 *              CMD0和CMD8的CRC值固定，其他命令CRC被忽略
 */
static uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg)
{
    uint8_t res;
    uint8_t retry = 0;
    uint8_t crc = 0X01; /* 默认CRC值(忽略CRC校验 + 停止位) */

    if (cmd & 0x80)     /* ACMD命令需要先发送CMD55前缀 */
    {
        cmd &= 0x7F;                    /* 清除最高位，获取实际ACMD命令索引 */
        res = sd_send_cmd(CMD55, 0);    /* 发送CMD55应用命令前缀 */

        if (res > 1)
        {
            return res;
        }
    }

    if (cmd != CMD12)   /* CMD12不需要重新选中，其他命令需要 */
    {
        sd_deselect();  /* 先取消上次片选，确保干净状态 */

        if (sd_select())
        {
            return 0xFF;/* SD卡选中失败 */
        }
    }

    /* 发送命令包(48位) */
    sd_spi_read_write_byte(cmd | 0x40); /* 起始位(01) + 命令索引(6位) */
    sd_spi_read_write_byte(arg >> 24);  /* 参数高字节[31:24] */
    sd_spi_read_write_byte(arg >> 16);  /* 参数[23:16] */
    sd_spi_read_write_byte(arg >> 8);   /* 参数[15:8] */
    sd_spi_read_write_byte(arg);        /* 参数低字节[7:0] */

    if (cmd == CMD0) crc = 0X95;        /* CMD0复位命令的CRC固定值 */

    if (cmd == CMD8) crc = 0X87;        /* CMD8接口条件命令的CRC固定值 */

    sd_spi_read_write_byte(crc);

    if (cmd == CMD12)                   /* CMD12停止传输命令特殊处理 */
    {
        sd_spi_read_write_byte(0xff);   /* CMD12需要额外的一个字节 */
    }


    retry = 10; /* 重试次数 */

    do          /* 等待有效响应(最高位为0) */
    {
        res = sd_spi_read_write_byte(0xFF);
    } while ((res & 0X80) && retry--); /* 最高位为1表示忙碌，继续等待 */

    return res; /* 返回状态值 */
}

/* ----------------------------------- 外部接口函数实现 ----------------------------------- */

/**
 * @brief       获取SD卡状态
 * @param       无
 * @return      操作结果
 * @retval      SD_OK       获取状态成功
 * @retval      SD_ERROR    获取失败
 * @note        发送ACMD13命令获取SD卡详细状态信息
 *              由于ACMD命令容易失败，最多重试20次
 */
uint8_t sd_get_status(void)
{
    uint8_t res;
    uint8_t retry = 20; /* 发送ACMD经常失败, 多尝试几次 */

    do
    {
        res = sd_send_cmd(ACMD13, 0);   /* 发ACMD13命令，获取状态 */
    }while(res && retry--);

    sd_deselect();      /* 取消片选 */

    return res;
}

/**
 * @brief       获取SD卡CID信息
 * @param       cid_data 存放CID数据的缓冲区指针(至少16字节)
 * @return      读取结果
 * @retval      SD_OK       CID信息读取成功
 * @retval      SD_ERROR    读取失败
 * @note        CID(卡识别数据)包含制造商ID、产品名称、序列号等信息
 *              发送CMD10命令读取16字节CID数据
 */
uint8_t sd_get_cid(uint8_t *cid_data)
{
    uint8_t res;

    res = sd_send_cmd(CMD10, 0);            /* 发CMD10命令，读CID */

    if (res == 0x00)
    {
        res = sd_receive_data(cid_data, 16);/* 接收16个字节的数据 */
    }

    sd_deselect();  /* 取消片选 */

    return res;
}

/**
 * @brief       获取SD卡CSD信息
 * @param       csd_data 存放CSD数据的缓冲区指针(至少16字节)
 * @return      读取结果
 * @retval      SD_OK       CSD信息读取成功
 * @retval      SD_ERROR    读取失败
 * @note        CSD(卡特定数据)包含容量、访问速度、块大小等信息
 *              发送CMD9命令读取16字节CSD数据
 */
uint8_t sd_get_csd(uint8_t *csd_data)
{
    uint8_t res;
    res = sd_send_cmd(CMD9, 0);             /* 发CMD9命令，读CSD */

    if (res == 0)
    {
        res = sd_receive_data(csd_data, 16);/* 接收16个字节的数据 */
    }

    sd_deselect();  /* 取消片选 */

    return res;
}

/**
 * @brief       获取SD卡总扇区数
 * @param       无
 * @return      SD卡总扇区数，0表示获取失败
 * @note        每个扇区固定为512字节
 *              根据SD卡版本使用不同的容量计算方法:
 *              - V2.0高容量卡(SDHC): 使用C_SIZE直接计算
 *              - V1.x/V2.0标准容量卡: 使用C_SIZE和C_SIZE_MULT计算
 */
uint32_t sd_get_sector_count(void)
{
    uint8_t csd[16];
    uint32_t capacity;
    uint8_t n;
    uint16_t csize;

    if (sd_get_csd(csd) != 0)       /* 取CSD信息，如果期间出错，返回0 */
    {
        return 0;                   /* 返回0表示获取容量失败 */
    }

    /* 根据CSD版本选择不同的容量计算方法 */
    if ((csd[0] & 0xC0) == 0x40)    /* CSD版本2.0(SDHC/SDXC卡) */
    {
        csize = csd[9] + ((uint16_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1;
        capacity = (uint32_t)csize << 10;       /* 容量 = (C_SIZE+1) × 1024 扇区 */
    }
    else    /* CSD版本1.0(标准容量卡/MMC卡) */
    {
        n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
        csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
        capacity = (uint32_t)csize << (n - 9);  /* 容量 = (C_SIZE+1) × 2^(n-9) 扇区 */
    }

    return capacity;    /* 返回扇区数量，实际字节容量需乘以512 */
}

/**
 * @brief       SD卡初始化
 * @param       无
 * @return      初始化结果
 * @retval      SD_OK       初始化成功
 * @retval      SD_ERROR    初始化失败
 * @note        SD卡初始化流程:
 *              1. 初始化SPI接口和片选引脚
 *              2. 发送至少74个时钟脉冲激活SD卡
 *              3. 发送CMD0使SD卡进入空闲状态
 *              4. 发送CMD8检测电压范围和卡版本
 *              5. 发送ACMD41或CMD1初始化卡
 *              6. 读取OCR寄存器确定卡类型
 *              7. 设置块大小为512字节(仅V1.x卡)
 */
uint8_t sd_init(void)
{
    uint8_t res;        /*  存放SD卡的返回值 */
    uint16_t retry;     /*  用来进行超时计数 */
    uint8_t ocr[4];
    uint16_t i;
    uint8_t cmd;

    sd_spi_init();      /* 初始化IO */
    sd_spi_speed_low(); /* 设置到低速模式 */

    for (i = 0; i < 10; i++)
    {
        sd_spi_read_write_byte(0XFF);       /* 发送至少74个时钟脉冲激活SD卡(10×8=80个) */
    }

    retry = 20;

    do
    {
        res = sd_send_cmd(CMD0, 0);         /* 发送CMD0使SD卡进入空闲状态 */
    } while ((res != 0X01) && retry--);    /* 等待响应0x01(空闲状态) */

    sd_type = 0;        /* 默认无卡 */

    if (res == 0X01)
    {
        if (sd_send_cmd(CMD8, 0x1AA) == 1)   /* 发送CMD8检测电压范围，响应1表示V2.0卡 */
        {
            for (i = 0; i < 4; i++)
            {
                ocr[i] = sd_spi_read_write_byte(0XFF);  /* 得到R7的32位响应 */
            }

            if (ocr[2] == 0X01 && ocr[3] == 0XAA)       /* 检查电压范围支持(2.7~3.6V) */
            {
                retry = 1000;

                do
                {
                    res = sd_send_cmd(ACMD41, 1UL << 30);   /* 发送ACMD41初始化，设置HCS位 */
                } while (res && retry--);

                if (retry && sd_send_cmd(CMD58, 0) == 0)    /* 鉴别SD2.0卡版本开始 */
                {
                    for (i = 0; i < 4; i++)
                    {
                        ocr[i] = sd_spi_read_write_byte(0XFF);  /* 得到OCR值 */
                    }

                    if (ocr[0] & 0x40)          /* 检查CCS位(卡容量状态) */
                    {
                        sd_type = SD_TYPE_V2HC; /* V2.0高容量卡(SDHC) */
                    }
                    else
                    {
                        sd_type = SD_TYPE_V2;   /* V2.0标准容量卡 */
                    }
                }
            }
        }
        else     /* 不支持CMD8，可能是SD V1.x或MMC卡 */
        {
            res = sd_send_cmd(ACMD41, 0);   /* 尝试发送ACMD41 */
            retry = 1000;

            if (res <= 1)
            {
                sd_type = SD_TYPE_V1;   /* SD V1.x卡 */
                cmd = ACMD41;           /* 使用ACMD41初始化 */
            }
            else     /* MMC卡不支持ACMD41命令 */
            {
                sd_type = SD_TYPE_MMC;  /* MMC V3卡 */
                cmd = CMD1;             /* 使用CMD1初始化 */
            }

            do
            {
                res = sd_send_cmd(cmd, 0);  /* 发送ACMD41或CMD1初始化 */
            } while (res && retry--);       /* 等待退出空闲模式 */

            if (retry == 0 || sd_send_cmd(CMD16, 512) != 0)
            {
                sd_type = SD_TYPE_ERR;      /* 初始化失败或设置块长度失败 */
            }
        }
    }

    sd_deselect();          /* 取消片选，释放SPI总线 */
    sd_spi_speed_high();    /* 切换到高速模式提高传输效率 */

    if (sd_type)            /* 卡类型有效，初始化成功 */
    {
        return SD_OK;
    }

    return res;
}

/**
 * @brief       SD卡扇区读取
 * @param       pbuf  数据缓存区指针
 * @param       saddr 起始扇区地址
 * @param       cnt   要读取的扇区数量
 * @return      读取结果
 * @retval      SD_OK       读取成功
 * @retval      SD_ERROR    读取失败
 * @note        FATFS和USB存储设备接口函数
 *              支持单扇区(CMD17)和多扇区(CMD18)读取
 *              SDHC卡使用扇区地址，其他卡使用字节地址
 *              多扇区读取后自动发送CMD12停止传输
 */
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{
    uint8_t res;
    long long lsaddr = saddr;

    if (sd_type != SD_TYPE_V2HC)
    {
        lsaddr <<= 9;   /* 标准容量卡需转换为字节地址(扇区号×512) */
    }

    if (cnt == 1)
    {
        res = sd_send_cmd(CMD17, lsaddr);       /* 单块读命令 */

        if (res == 0)   /* 指令发送成功 */
        {
            res = sd_receive_data(pbuf, 512);   /* 接收512个字节 */
        }
    }
    else
    {
        res = sd_send_cmd(CMD18, lsaddr);       /* 连续读命令 */

        do
        {
            res = sd_receive_data(pbuf, 512);   /* 接收512个字节 */
            pbuf += 512;
        } while (--cnt && res == 0);

        sd_send_cmd(CMD12, 0);  /* 发送停止命令 */
    }

    sd_deselect();  /* 取消片选 */
    return res;
}

/**
 * @brief       SD卡扇区写入
 * @param       pbuf  数据缓存区指针
 * @param       saddr 起始扇区地址
 * @param       cnt   要写入的扇区数量
 * @return      写入结果
 * @retval      SD_OK       写入成功
 * @retval      SD_ERROR    写入失败
 * @note        FATFS和USB存储设备接口函数
 *              支持单扇区(CMD24)和多扇区(CMD25)写入
 *              SDHC卡使用扇区地址，其他卡使用字节地址
 *              多扇区写入前发送ACMD23设置预擦除块数(仅SD卡)
 *              多扇区写入后发送停止传输令牌0xFD
 */
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{
    uint8_t retry = 20; /* 发送ACMD经常失败, 多尝试几次 */
    uint8_t res;
    long long lsaddr = saddr;

    if (sd_type != SD_TYPE_V2HC)
    {
        lsaddr <<= 9;   /* 标准容量卡需转换为字节地址(扇区号×512) */
    }

    if (cnt == 1)
    {
        res = sd_send_cmd(CMD24, lsaddr);   /* 单块写命令 */

        if (res == 0)   /* 指令发送成功 */
        {
            res = sd_send_block(pbuf, 0xFE);/* 写512个字节 */
        }
    }
    else
    {
        if (sd_type != SD_TYPE_MMC)
        {
            do
            {
                res = sd_send_cmd(ACMD23, cnt);   /* 发送ACMD23设置预擦除块数 */
            }while(res && retry--);
        }

        res = sd_send_cmd(CMD25, lsaddr);   /* 连续写命令 */

        if (res == 0)
        {
            do
            {
                res = sd_send_block(pbuf, 0xFC);    /* 多块写入数据令牌 */
                pbuf += 512;
            } while (--cnt && res == 0);

            res = sd_send_block(0, 0xFD);   /* 发送停止传输令牌 */
        }
    }

    sd_deselect();/* 取消片选 */
    return res;
}

/**
 * @brief       通过串口打印SD卡相关信息
 * @param       无
 * @retval      无
 */
void show_sdcard_info(void)
{
    uint8_t csd[16];
    uint8_t cid[16];
    uint32_t capacity;
    
    /* 检查SD卡是否初始化成功 */
    if (sd_init() != SD_OK)
    {
        printf("SD Card Init Failed!\r\n");
        return;
    }
    
    /* 显示SD卡类型信息 */
    printf("-----------------SD Card Information-----------------\r\n");
    switch (sd_type)
    {
        case SD_TYPE_ERR:
            printf("Card Type: Error/Unknown\r\n");
            return;
        case SD_TYPE_MMC:
            printf("Card Type: MMC Card\r\n");
            break;
        case SD_TYPE_V1:
            printf("Card Type: SD V1.x (SDSC)\r\n");
            break;
        case SD_TYPE_V2:
            printf("Card Type: SD V2.0 (SDSC)\r\n");
            break;
        case SD_TYPE_V2HC:
            printf("Card Type: SD V2.0 (SDHC/SDXC)\r\n");
            break;
        default:
            printf("Card Type: Unknown Type\r\n");
            break;
    }
    
    /* 获取并显示CSD寄存器信息 */
    if (sd_get_csd(csd) == SD_OK)
    {
        /* 根据SD卡类型计算容量 */
        if (sd_type == SD_TYPE_V2HC)
        {
            /* SDHC/SDXC卡容量计算 */
            capacity = ((uint32_t)csd[7] & 0x3F) << 16;
            capacity |= (uint32_t)csd[8] << 8;
            capacity |= (uint32_t)csd[9];
            capacity = (capacity + 1) * 512;  /* 单位：KB */
        }
        else
        {
            /* SD V1.x/V2.0标准容量卡计算 */
            uint32_t c_size, c_size_mult, read_bl_len;
            
            c_size = ((uint32_t)(csd[6] & 0x03) << 10) | ((uint32_t)csd[7] << 2) | ((csd[8] & 0xC0) >> 6);
            c_size_mult = ((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7);
            read_bl_len = csd[5] & 0x0F;
            
            capacity = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len) / 1024;  /* 单位：KB */
        }
        
        printf("Card Capacity: %lu KB (%lu MB)\r\n", capacity, capacity / 1024);
        printf("Block Size: 512 bytes\r\n");
        printf("Total Blocks: %lu\r\n", capacity * 2);  /* 512字节块数 */
    }
    else
    {
        printf("Failed to read CSD register\r\n");
    }
    
    /* 获取并显示CID寄存器信息 */
    if (sd_get_cid(cid) == SD_OK)
    {
        printf("Manufacturer ID: 0x%02X\r\n", cid[0]);
        printf("OEM/Application ID: %c%c\r\n", cid[1], cid[2]);
        printf("Product Name: %c%c%c%c%c\r\n", cid[3], cid[4], cid[5], cid[6], cid[7]);
        printf("Product Revision: %d.%d\r\n", (cid[8] >> 4) & 0x0F, cid[8] & 0x0F);
        printf("Serial Number: 0x%02X%02X%02X%02X\r\n", cid[9], cid[10], cid[11], cid[12]);
        printf("Manufacturing Date: %d/%d\r\n", 
               2000 + (((cid[13] & 0x0F) << 4) | ((cid[14] & 0xF0) >> 4)), 
               cid[14] & 0x0F);
    }
    else
    {
        printf("Failed to read CID register\r\n");
    }
    
    printf("-----------------------------------------------------\r\n\r\n");
}
